/**
 * mLibLineSegment.js
 * Created: April 2013
 * Source link: http://code.google.com/p/code-depository/source/browse/
 *
 * By Suresh Nalluri,
 * License: The MIT License (MIT)
 *          See LICENSE details
 *
 *
 *	Todo:
 *			transform TBD
 *
 * Requires:
 *	mLibFactory.js
 *	mLibIncludes.js
 *	mLibVec3.js
 *	mLibLin3.js
 *	mLIB.Quaternion,
 *  mLIB.Mat44;
 *
 * 3D LineSegment Class
 * A LineSegment object is composed of a starting point, designated the origin, and
 * an un-normalised Direction vector.  Basically a line segment has a start and end
 * points, unlike a Line object, which has a start point but no end.
 *
 * note the components themselves are objects
 *
 * note this constructor pattern allows us to create objects in 2 ways:
 * 1. var o = mLib.lin3( 0 or more params );      //OR
 * 2. var o = new mLib.lin3( 0 or more params );
 *
 * NOTE: var o = [new] mLIB.Lin3(<no params>); // will return default line composed of the
 * unit vector in direction i (1,0,0), and the Origin (0,0,0) as the starting point.
 *
 *
 * added benefit of this pattern is that we get a copy constructor by default!!
 * eg. var newLine = mLIB.Lin3Seg(existingLine);
 * so no need to have an explicit copy method.
 *
 * Usage:
 *		var l = [new] mLIB.Lin3Seg([ lineSeg | p0, p1 | origin, direction | <noargs> ]);
 *
 *-------------------------------------------------------
 **/
 

if (typeof mLIB == 'undefined') function mLIB() {};

( function(module) {
	var Lin3 = module.Lin3,
        Lin3Seg = module.Lin3Seg,
        Mat44 = module.Mat44,
        Vec3 = module.Vec3,
        Point3 = module.Point3;

	module.Lin3Seg = Factory.obj( {
		init: function() {
			if ( !arguments.length ) {
				//default if no params present
				this.origin = module.Point3(0.0, 0.0, 0.0);
			  	this.direction = module.Vec3(1.0, 0.0, 0.0);
			} else if ( arguments[0].isClass(module.Lin3Seg) || arguments[0].isClass(module.Lin3) ) {
				// called with 1 param - existing Lin3Seg (ie return a copy), or the param is
				// an existing or Lin3.
				this.origin = module.Point3(arguments[0].origin);
				this.direction = module.Vec3(arguments[0].direction );
			} else if ( arguments[0].isClass(module.Point3) && arguments[1].isClass(module.Point3) ) {
				// called with 2 params - points p0 and p1
				// *** note if p1 is really a direction vector, we return rubbish as we have
				// *** no way of distinguishing a point from a vector.
				
				this.origin = module.Point3(arguments[0]);
				this.direction = module.Vec3( arguments[1] ).minus(this.origin);
			}
		},  //init()

		// apply transform to line segemnt - see Lin3.transform for notes on efficiency
		// requires the Quaternion and Mat44 libs
        transform: function(scale, rQuat, translateVec) {
            var origin, direction,
                transOp;
            
            // form the transformation matrix from the quaternion and the scale factor
            // note this is probably not the most efficient way but at least it doesn't
            // break any oo rules.
            transOp = new Mat44(rQuat).multiply(Mat44().scaling(Vec3(scale, scale, scale));

            // transform the direction;
            direction = transOp.Transform(this.direction).normalise();

            // apply the translation vector to the matrix
            // again note: not the most efficient way of doing it, but no oo rules broken
            transOP.addTranslation(translateVec);

            // transform the origin
            origin = transOp.Transform(this.origin);
            
            return Lin3Seg(origin, direction);

        },
		
		// legth of the line segment.. nore direction vector is unormalised
		length: function() {
			return this.direction.length();
		},
		
		// the distance between the 2 end points squared
		lengthSquared: function() {
			return this.direction.lengthSquared();
		},

		// equality tests
		isEqual: function( seg ) {
			var thisEndPoint = this.origin.Plus(this.direction),
				segEndPoint  = seg.origin.Plus(seg.direction);
			
			// ( (Lo == So && Ld == Sd) || (Lo == So+Sd && Ld == -Sd) )
			// or put another way: ( P0 == Q0 && P1 == Q1 ) || ( P0 == Q1 && P1 == Q0 )
			return (
				( this.origin.isEqual(seg.origin) && thisEndPoint.isEqual(segEndPoint) ) ||
				( this.origin.isEqual(segEndPoint) && tthisEndPoint.isEqual(seg.origin) )
			);
		},
		
		isNotEqual: function( seg ) {
			var thisEndPoint = this.origin.Plus(this.direction),
				segEndPoint  = seg.origin.Plus(seg.direction);

			// ( (Lo == So && Ld == Sd) || (Lo == So+Sd && Ld == -Sd) )
			// or put another way: ! ( ( P0 == Q0 && P1 == Q1 ) || ( P0 == Q1 && P1 == Q0 ) )
			return !(
				( this.origin.isEqual(seg.origin) && thisEndPoint.isEqual(segEndPoint) ) ||
				( this.origin.isEqual(segEndPoint) && tthisEndPoint.isEqual(seg.origin) )
			);
		},

		// given a point Q, return the closest point on line segment to Q
		// Note: line segment is conceptually described as having P0 and P1 endpoint
		// where P0 is the origin Lo, and P1 is Lo + Ld
		ClosestPoint: function( Q ) {
			var w = Q.Minus(this.origin),
				prj = w.dot(this.direction);
				
			if ( prj <= 0.0 ) {
				// the starting point P0 of this line seg is closest
				return this.origin.Copy();
			} else {
				var vsq = this.direction.lengthSquared();  // same as Ld.Ld !!
				if ( prj >= vsq ) {
					// P1 is the closest : Lo + Ld
					return this.origin.Plus(this.direction);
				} else {
					// it is somewhere in the segment: L0 + s*L
					return this.direction.ScaleUp(prj/vsq).plus(this.origin);
				}
			}
		},
		
		// get the closest point between this line segment and the entity e
		ClosestPoints: function(e) {
			if ( e.isClass(module.Lin3Seg) ) {
				// between this line segment S0 and S1
				return closestPointsSegSeg(this, e);
			} else if ( e.isClass(module.Lin3) ) {
				// between this Line segment S and a Line L
				return closestPointsSegLine(this, e);
			}
			else if ( e.isClass(module.Ray3) ) {
				// between this Line Segment S and a Ray R
				return closestPointsSegRay(this, e);
			}
			
			// unknown parameter types
			return null;
		},
				
		// get the distance sqrd of the distance between the 2 closest points, between this line
		// and the given entity e 
		distanceSquared: function( e ) {
			if ( e.isClass(module.Point3) ) {
				// between this Line Segment S and a Point P
				return disSqSegPoint(this, e);
			} else if ( e.isClass(module.Lin3Seg) ) {
				// between this line segment S0 and a line segment S1
				return disSqSegSeg(this, e);
			}
			else if ( e.isClass(module.Lin3) ) {
				// between this Line segment S and a Line L
				return disSqSegLine(this, e);
			}
			else if ( e.isClass(module.Ray3) ) {
				// between this Line Segment S and a Ray R
				return disSqSegRay(this, e);
			}
			
			// unknown parameter types
			return null;
		},
		
		
		// debug
		log: function() {
			return ['{ o:', this.origin.log(), ' , d:', this.direction.log(), '}'].join('');
		},
		
		toArray: function() {
			return [this.origin.toArray(), this.direction.toArray()].join('');
		},

		// accessor and setter methods
		get: function(out) {
            // put the 2 end point into the returnContainer object
            out.P0 = this.origin;
            out.P1 = this.origin.Plus(this.direction);  // P0 + Ld
            
            return this;  // let's return this anyway.. for chaining purposes
        },
		
		set: function(P0, P1) {
			this.origin = module.Point3(P0);
			this.direction = module.Point3(P1).minus(P0);  // original: = this.origin.Minus(P0);
			return this;
		}
	} ); //module.Lin3Seg = Factory.obj

	/**
	 * internal helper functions.
	 **/

	// distance squared between 2 line segs
	function disSqSegSeg(l1, l2) {
		var sC, sCn, sCd, tC, tCn, tCd, wc,
			w0 = l1.origin.Minus(l2.origin),
			a = l1.direction.dot(l2.direction),	// u.v
			b = l2.direction.dot(l2.direction),	// v.v
			c = l1.direction.dot(l1.direction),	// u.u
			d = w0.dot(l2.direction),			// w0.v
			e = w0.dot(l1.direction),			// w0.u
			denom = a*a - b*c;
	
		// Note.	sC = sCn / sCd
		//			tC = tCn / tCd
				
		if ( module.Includes.isZero(denom) ) {
			// parallel segments, as per parallel lines we start with sC set to 0
			sCd = tCd = b;
			sCn = 0.0;
			tCn = d;
		} else {	// restrict sC to [0,1]
			// sC = (eb – ad) / (a^2 – bc)
			sCn = e*b - a*d;	// sC = ( eb – ad ) / ( a^2 – bc )
			tCn = e*a - d*c;	// tC = ( ea – dc ) / ( a^2 – bc )
			sCd = tCd = denom;

			if ( (sCn < 0.0 && sCd > 0.0) || (sCn > 0.0 && sCd < 0.0) ) {
				// sC < 0.0 restrict sC to 0
				sCn = 0;
				tCn = d;
				tCd = b;
			} else if ( (sCn > 0 && sCd > 0 && sCn > sCd) || (sCn < 0 && sCd < 0 && sCn < sCd) ) {	// restrict sC to 1  
				// sC > 0 restrict sC to 1
				sCn = sCd;		// forces sC to be 1.0
				tCn = a + d;
				tCd = b;
			}
		}
			
	
		// restrict tC to [0,1]
		if ( (tCn < 0.0 && tCd > 0.0) || (tCn > 0.0 && tCd < 0.0) ) {
			// tC < 0.0 restrict tC to 0
			tCn = 0.0;
			//sc = –e/c
			if ( -e < 0.0 ) { 	// restrict sC to 0
				sC= 0;
			} else if ( -e > c )	{ //restrict sC to 1
				sC = 1.0;
			} else {
				sC = -e/c;
			}
		} else if ( (tCn > 0 && tCd > 0 && tCn > tCd) || (tCn < 0 && tCd < 0 && tCn < tCd) ) {	// restrict sC to 1  
			// tC > 1 restrict tC to 1
			tC = 1.0;
			
			// sC = (a–e)/c  < 0
			if ( (a-e) < 0.0 ) { 	// restrict sC to 0
				sC = 0.0;
			} else if ( (a-e) > 1.0 ) { // retrict sC to 1
				sC = 1.0;
			} else {
				sC = (a-e)/c;
			}
		} else {
			tC = tCn / tCd;
			sC = sCn / sCd;
		}

		// get the wC vector, ie.  w0 + sC*L1d - tC*L2d
		wc = w0.plus(l1.direction.ScaleUp(sC)).minus(l2.direction.ScaleUp(tC));
		return wc.lengthSquared();	// same as wc.dot(wc)

	}  // helper disSqSegSeg()

	// distance squared between a line seg and a ray
	function disSqSegRay(seg ,ray) {
		var sC, sCn, sCd, tC, tCn, tCd, wc,
			w0 = seg.origin.Minus(ray.origin),
			a = seg.direction.dot(ray.direction),	// u.v
			// b = ray.direction.dot(ray.direction) // v.v should always be 1 since v is the unit direction vector
			c = seg.direction.dot(seg.direction),	// u.u
			d = w0.dot(ray.direction),				// w0.v
			e = w0.dot(seg.direction),				// w0.u
			denom = a*a - c;		// denom = a*a-b*c; but since  b=1, simplify to denom = a*a-c;

		// Note.	sC = sCn / sCd
		//			tC = tCn / tCd
		
		if ( module.Includes.isZero(denom) ) {
			// parallel segments, as per parallel lines we start with sC set to 0
			sCd = tCd = 1; //  = b, and b = 1.
			sCn = 0.0;
			tCn = d;
			
		} else {	// restrict sC to [0,1]			
			sCn = e - a*d;		// sC=(eb–ad)/(a^2–bc), b=1. so sC=(e–ad)/(a^2–bc)
			tCn = e*a - d*c;	// tC=(ea–dc)/(a^2–bc)
			sCd = tCd = denom;
			
			if ( (sCn < 0.0 && denom >0.0) || (sCn >0.0 && denom < 0.0) ) {
				// sC < 0.0, set it to 0
				sCn = 0;
				tCn = d;
				tCd = 1; // =b and b = 1;
				
			} else if ( (sCn > 0 && denom > 0 && sCn > denom) || (sCn < 0 && denom < 0 && sCn < denom) ) {
				// sC >1 so, ensure it's 1
				sCn = sCd;		// forces sC to be 1.0
				tCn = a + d;
				tCd = 1; // = b and b = 1;
			}
		}

		// restrict tC to >= 0; remember a ray is infinite starting at its origin and continuing
		// to infinity along its direction vector
		if ( (tCn < 0.0 && tCd >0.0) || (tCn >0.0 && tCd < 0.0) ) {
			// tC < 0, restrict it to 0
			tCn = 0.0;
			
			//sc = –e/c; if sc < 0 restrict to 0; if sc >1, restrict to 1
			sC = -e < 0.0 ? 0.0 : -e > c ? 1.0 : -e/c;
			
		} else {
			tC = tCn / tCd;
			sC = sCn / sCd;
		}

		// get the wC vector, ie.  w0 + sC*Sd - tC*Rd
		wc = w0.plus(seg.direction.ScaleUp(sC)).minus(ray.direction.ScaleUp(sC));
		return wc.lengthSquared();	// same as wc.dot(wc)

	}  //helper disSqSegRay()

	// distance squared between a line seg and a line
	function disSqSegLine(seg, line) {
		var sC, sCn, tC, wC,
			w0 = seg.origin.Minus(line.origin),
			a = seg.direction.dot(line.direction),	// u.v
			b = line.direction.dot(line.direction),	// v.v
			c = seg.direction.dot(seg.direction),	// u.u
			d = w0.dot(line.direction),				// w0.v
			e = w0.dot(seg.direction),				// w0.u
			denom = a*a - b*c;
	
		// Note.	sC = sCn / sCd
		//			tC = tCn / tCd
		
		if ( module.Includes.isZero(denom) ) {
			// parallel lines, the 2 points are P0 from segment & point Qc on line closest to P0
			// ie. Pc = L2o  and Qc = L2o + tC*Ld
			
			// get the wC vector, ie.  w0 + sC*Sd - tC*Ld (note sC = 0.0 and tC = d/b)
			
			wC = w0.minus(line.direction.ScaleUp(d/b));
			return wC.lengthSquared();	// same as wc.wc ie. wc.dot(wc)

		} else {
			sCn = e*b - a*d;
			
			//restrict sC to [0,1]
			if ( (sCn < 0.0 && denom >0.0) || (sCn >0.0 && denom < 0.0) ) {
				sC = 0.0;
				tC = d/b;
			} else if ( (sCn > 0 && denom > 0 && sCn > denom) || (sCn < 0 && denom < 0 && sCn < denom) ) {
				sC = 1.0;
				tC = (a+d) / b;
			} else {
				sC = sCn / denom;
				tC = (e*a - d*c) / denom;
			}

			// get the wC vector, ie.  w0 + sC*Sd - tC*Ld
			wC = w0.plus(seg.direction.ScaleUp(sC)).minus(line.direction.ScaleUp(tC));
			return wC.lengthSquared();	// same as wc.wc ie. wc.dot(wc)
		}
		
	} //helper disSqSegLin()

	// distance squared between a line seg and a point
	function disSqSegPoint(seg, Q) {

		var w = Q.Minus(seg.origin),
			wDotw = w.lengthSquared(), 
			prj = w.dot(seg.direction);

		if ( prj <= 0.0 ) {
			// the starting point P0 of this line seg is closest
			return wDotw;
		} else
		{
			var vDotv = seg.direction.lengthSquared();  // same as seg.direction.dot(seg.direction)
			if ( prj >= vDotv ) {
				// P1 is the closest : Lo + Ld
				// w.w – 2.w.v + v.v
				return wDotw - 2*prj + vDotv;
			} else
			{
				// it is somewhere in the segment: L0 + s*L
				// ||w||^2 – (w.v)^2/ v.v
				return wDotw - prj*prj/vDotv;
			}
		}
	} //helper disSqSegPoint()

	// closest points between 2 line segs
	function closestPointsSegSeg(l1, l2) {
		var sC, sCn, sCd, tC, tCn, tCd,
			w0 = l1.origin.Minus(l2.origin),
			a = l1.direction.dot(l2.direction),	// u.v
			b = l2.direction.dot(l2.direction),	// v.v
			c = l1.direction.dot(l1.direction),	// u.u
			d = w0.dot(l2.direction),			// w0.v
			e = w0.dot(l1.direction),			// w0.u
			denom = a*a - b*c;
	
		// Note.	sC = sCn / sCd
		//			tC = tCn / tCd

		if ( module.Includes.isZero(denom) ) {
			// parallel segments, as per parallel lines we start with sC set to 0
			sCd = tCd = b;
			sCn = 0.0;
			tCn = d;
		} else {	// restrict sC to [0,1]
			// sC = (eb – ad) / (a^2 – bc)
			sCn = e*b - a*d;	// sC = ( eb – ad ) / ( a^2 – bc )
			tCn = e*a - d*c;	// tC = ( ea – dc ) / ( a^2 – bc )
			sCd = tCd = denom;

			if ( (sCn < 0.0 && sCd > 0.0) || (sCn > 0.0 && sCd < 0.0) ) {
				// sC < 0.0 restrict sC to 0
				sCn = 0.0;
				tCn = d;
				tCd = b;

			} else if ( (sCn > 0 && sCd > 0 && sCn > sCd) || (sCn < 0 && sCd < 0 && sCn < sCd) ) {	// restrict sC to 1  
				// sC > 0 restrict sC to 1
				sCn = sCd;		// forces sC to be 1.0
				tCn = a + d;
				tCd = b;
			}
		}
			
		// restrict tC to [0,1]
		// 
		if ( (tCn < 0.0 && tCd > 0.0) || (tCn > 0.0 && tCd < 0.0) ) {
			// tC < 0.0 restrict tC to 0
			tC = 0.0;
			
			//sc = –e/c
			sC = -e<0.0 ? 0.0 : -e>c ? 1.0 : -e/c;
			/*
				if ( -e < 0.0 ) { 	// restrict sC to 0
					sC= 0.0;
				} else if ( -e > c )	{ //restrict sC to 1
					sC = 1.0;
				} else {
					sC = -e/c;
				}
			*/
		} else if ( (tCn > 0 && tCd > 0 && tCn > tCd) || (tCn < 0 && tCd < 0 && tCn < tCd) ) {	// restrict sC to 1  
			// tC > 1 restrict tC to 1
			tC = 1.0;
			
			// sC = (a–e)/c  < 0
			sC = (a-e)<0.0 ? 0.0 : (a-e)>c ? 1.0 : (a-e)/c;
			/*
				if ( (a-e) < 0.0 ) { 	// restrict sC to 0
					sC = 0.0;
				} else if ( (a-e) > c ) { // retrict sC to 1 --------------- was > 1.0
					sC = 1.0;
				} else {
					sC = (a-e)/c;
				}
			*/
		} else {
			tC = tCn / tCd;
			sC = sCn / sCd;
		}

		// closest points !!
		return {
			Pc: l1.direction.ScaleUp(sC).plus(l1.origin),	// l1.origin.Plus(l1.direction.ScaleUp(sC)) - creates 2 objects!
			Qc: l2.direction.ScaleUp(tC).plus(l2.origin)	//l2.origin.Plus(l2.direction.ScaleUp(tC)) - creates 2 objects!
		};
	
	} //helper closestPointsSegSeg()

	// closest points between line seg & line
	function closestPointsSegLine(seg, line) {
		var sC, sCn, tC,
			w0 = seg.origin.Minus(line.origin),
			a = seg.direction.dot(line.direction),	// u.v
			b = line.direction.dot(line.direction),	// v.v
			c = seg.direction.dot(seg.direction),	// u.u
			d = w0.dot(line.direction),				// w0.v
			e = w0.dot(seg.direction),				// w0.u
			denom = a*a - b*c;
	
		// Note.	sC = sCn / sCd
		//			tC = tCn / tCd
		
		if ( module.Includes.isZero(denom) ) {
			// parallel lines, let's just return P0 from segment & point Qc on line closest to P0
			// compute closest points
			return { 
				Pc: seg.origin.Copy(),
				Qc: line.direction.ScaleUp(d/b).plus(line.origin)
			};
		} else {
			sCn = e*b - a*d;	// sC = ( eb – ad ) / ( a^2 – bc )
			
			//restrict sC to [0,1] ( ie restrict since sC applies to L1 - a line segment)
			if ( (sCn < 0.0 && denom >0.0) || (sCn >0.0 && denom < 0.0) ) {
				sC = 0.0;
				tC = d/b;
			} else if ( (sCn > 0 && denom > 0 && sCn > denom) || (sCn < 0 && denom < 0 && sCn < denom) ) {
				sC = 1.0;
				tC = (a+d) / b;
			} else {
				sC = sCn / denom;
				tC = (e*a - d*c) / denom;
			}
			
			// note we didn't restrict tC as it applies to L2 - a line !
			
			return {
				Pc: seg.direction.ScaleUp(sC).plus(seg.origin),  // So + sC*Sd
				Qc: line.direction.ScaleUp(tC).plus(line.origin) // Lo + tC*Ld
			};
		}
		
	} //helper closestPointsSegLine()

	// closest points between line seg & ray
	function closestPointsSegRay(seg, ray) {
		var sC, sCn, sCd, tC, tCn, tCd,
			w0 = seg.origin.Minus(ray.origin),
			a = seg.direction.dot(ray.direction),	// u.v
			// b = ray.direction.dot(ray.direction) // v.v should always be 1 since R2d is the unit direction vector
			c = seg.direction.dot(seg.direction),	// u.u
			d = w0.dot(ray.direction),				// w0.v
			e = w0.dot(seg.direction),				// w0.u
			denom = a*a - c;		// denom = a*a-b*c; but since  b=1, simplify to denom = a*a-c;

		// Note.	sC = sCn / sCd
		//			tC = tCn / tCd
		
		if ( module.Includes.isZero(denom) ) {
			// parallel segments, as per parallel lines we start with sC set to 0
			sCd = tCd = b;
			sCn = 0.0;
			tCn = d;
		} else {	// restrict sC to [0,1]
			// sC=(eb–ad)/(a^2–bc) , b=1, so sC=(e-ad)/(a^2-c)
			sCn = e - a*d;
			sCd = denom;
			
			// tC=(ea–dc)/(a^2–bc)
			tCn = e*a - d*c;
			tCd = denom;
			
			if ( (sCn < 0.0 && denom >0.0) || (sCn >0.0 && denom < 0.0) ) {
				// sC < 0, restrict to 0
				sCn = 0;
				tCn = d;
				tCd = b;
			} else if ( (sCn > 0 && denom > 0 && sCn > denom) || (sCn < 0 && denom < 0 && sCn < denom) ) {
				// sC > 1, restrict to 1
				sCn = sCd;		// forces sC to be 1.0
				tCn = a + d;
				tCd = 1; // = b and b = 1;
			}
		}

		// restrict tC to >= 0; remember a ray is infinite starting ay its origin and continuing
		// to infinity along its direction vector
		if ( (tCn < 0.0 && tCd > 0.0) || (tCn > 0.0 && tCd < 0.0) ) {
			// tC < 0, restrict to 0
			tCn = 0.0;

			//sc = –e/c; if sc < 0 restrict to 0; if sc >1, restrict to 1
			sC = -e < 0.0 ? 0.0 : -e > c ? 1.0 : -e/c;
			/*
				if ( -e < 0.0 ) { 	// restrict sC to 0
					sC= 0;
				} else if ( -e > c )	{ //restrict sC to 1
					sC = 1.0;
				} else {
					sC = -e/c;
				}
			*/
		} else {
			tC = tCn / tCd;
			sC = sCn / sCd;
		}

		return {
			Pc: seg.direction.ScaleUp(sC).plus(seg.origin),// So + sC*Sd
			Qc: ray.direction.ScaleUp(tC).plus(ray.origin) // Ro + tC*Rd
		};
			
	} //helper closestPointsSegRay()

    // friend methods ---------------------------------------------
    //
	var friends = {
		// distance squared between 2 entities
		distanceSquared: function( e0, e1 ) {
			if ( e0.isClass(module.Lin3Seg) && e1.isClass(module.Lin3Seg) ) {
				// 1. between 2 line segments S0 and S1
				return disSqSegSeg(e0, e1);
			}
			else if ( e0.isClass(module.Lin3Seg) && e1.isClass(module.Ray3) ) {
				// 2. between a Line Segment S and a Ray R
				return disSqSegRay(e0, e1);
			}
			else if ( e0.isClass(module.Ray3) && e1.isClass(module.Lin3Seg) ) {
				// 3. between a Ray R and a Segment S (same as 2 but params swapped
				return disSqSegRay(e1, e0);
			}
			else if ( e0.isClass(module.Lin3Seg) && e1.isClass(module.Lin3) ) {
				// 4. between a Line segment S and a Line L
				return disSqSegLine(e0, e1);
			}
			else if ( e0.isClass(module.Lin3) && e1.isClass(module.Lin3Seg) ) {
				// 5. between a Line L and a Line Segment S (same as 4 but params swapped)
				return disSqSegLine(e1, e0);
			}
			else if ( e0.isClass(module.Lin3Seg) && e1.isClass(module.Point3) ) {
				// 6. between a Line Segment S and a Point P
				return disSqSegPoint(e0, e1);
			}
			else if ( e0.isClass(module.Point3) && e1.isClass(module.Lin3Seg) ) {
				// 7. between a Point P and a Line Segment S (same as 6 but params swapped)
				return disSqSegPoint(e1, e0);
			}
			
			// unknown parameter types
			return null;
		},
		
		ClosestPoints: function(e0, e1) {
			if ( e0.isClass(module.Lin3Seg) && e1.isClass(module.Lin3Seg) ) {
				// 1. between 2 line segments S0 and S1
				return closestPointsSegSeg(e0, e1);
			}
			else if ( e0.isClass(module.Lin3Seg) && e1.isClass(module.Ray3) ) {
				// 2. between a Line Segment S and a Ray R
				return closestPointsSegRay(e0, e1);
			}
			else if ( e0.isClass(module.Ray3) && e1.isClass(module.Lin3Seg) ) {
				// 3. between a Ray R and a Segment S (same as 2 but params swapped
				return closestPointsSegRay(e1, e0);
			}
			else if ( e0.isClass(module.Lin3Seg) && e1.isClass(module.Lin3) ) {
				// 4. between a Line segment S and a Line L
				return closestPointsSegLine(e0, e1);
			}
			else if ( e0.isClass(module.Lin3) && e1.isClass(module.Lin3Seg) ) {
				// 5. between a Line L and a Line Segment S (same as 4 but params swapped)
				return closestPointsSegLin(e1, e0);
			}
			
			// unknown parameter types
			return null;
		}
		
	} //friends{}

    for (var f in friends) {
        friends.hasOwnProperty(f) && (module.Lin3Seg[f] = friends[f]);
    };

} )(mLIB);
